import AsyncValidator from 'async-validator';
import { Toast } from 'vant';

Toast.allowMultiple();

const utils = {
  validate(model, rules, callback, options) {
    const initOptions = {
      showMessage: true,
    };

    options = { ...initOptions, ...(options || {}) };

    if ((!rules || rules.length === 0) && callback) {
      callback(true, null);
      return true;
    }

    let errors = [];
    const props = Object.keys(rules);
    let count = 0;

    for (const i in props) {
      if (Object.prototype.hasOwnProperty.call(props, i)) {
        const prop = props[i];
        const value = utils.getValueByProp(model, prop);

        utils.validateItem(rules, prop, value, (err) => {
          if (err && err.length > 0) {
            errors = errors.concat(err);
          }

          count++;

          if (count === props.length) {
            if (errors.length > 0) {
              if (options.showMessage) {
                utils.showToast(errors[0].message);
              }
              callback(false, errors);
            } else {
              callback(true, null);
            }
          }
        });
      }
    }
  },

  validateField(model, rules, props, callback, options) {
    const initOptions = {
      showMessage: true,
    };

    options = { ...initOptions, ...(options || {}) };
    props = [].concat(props);

    if (props.length === 0) {
      return;
    }

    let errors = [];
    let count = 0;

    for (const i in props) {
      if (Object.prototype.hasOwnProperty.call(props, i)) {
        const prop = props[i];
        const value = utils.getValueByProp(model, prop);

        utils.validateItem(rules, prop, value, (err) => {
          if (err && err.length > 0) {
            errors = errors.concat(err);
          }

          count++;

          if (count === props.length) {
            if (errors.length > 0) {
              if (options.showMessage) {
                utils.showToast(errors[0].message);
              }
              callback(false, errors);
            } else {
              callback(true, null);
            }
          }
        });
      }
    }
  },

  validateItem(rules, prop, value, callback) {
    if (!rules || JSON.stringify(rules) === '{}') {
      if (callback instanceof Function) {
        callback();
      }
      return true;
    }

    const propRules = [].concat(rules[prop] || []);

    propRules.forEach((rule) => {
      if (rule.pattern) {
        rule.pattern = new RegExp(rule.pattern);
      }
    });

    const descriptor = {
      [prop]: propRules,
    };
    const validator = new AsyncValidator(descriptor);
    const model = {
      [prop]: value,
    };

    validator.validate(
      model,
      {
        firstFields: true,
      },
      (errors) => {
        callback(errors);
      },
    );
  },

  getValueByProp(obj, prop) {
    let tempObj = obj;

    prop = prop.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');

    const keyArr = prop.split('.');
    let i = 0;

    for (let len = keyArr.length; i < len - 1; ++i) {
      if (!tempObj) break;

      const key = keyArr[i];

      if (key in tempObj) {
        tempObj = tempObj[key];
      } else {
        break;
      }
    }

    return tempObj
      ? typeof tempObj[keyArr[i]] === 'string'
        ? tempObj[keyArr[i]].trim()
        : tempObj[keyArr[i]]
      : null;
  },

  showToast(message) {
    Toast.clear();
    Toast({
      position: 'bottom',
      message,
    });
  },
};

export default utils;
